package v1;

//First working Draft of the Quick-and-Dirty Implementation, by MohamadJB from http://kgdug.org
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class QD extends JPanel implements MouseListener
, MouseMotionListener, ComponentListener, KeyListener,Runnable {

 JFrame frame;
 Thread thread;
 static int kills=0,lives=0,level=0; 
 static GO player;
 static InvaderGOs invaders;
 static BulletGOs bullets;

 public QD(){frame=new JFrame(
		"Space Invaders from http://kgdug.org, QuickAndDirtyImplementation by MohamadJB");
	frame.setContentPane(this);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.setBounds(10,10, 800, 600);
	player=new GO();
	invaders=new InvaderGOs();invaders.init(10,20);
	bullets=new BulletGOs();bullets.init(15);
	addMouseListener(this);
	addMouseMotionListener(this);
	addKeyListener(this);
	addComponentListener(this);
	frame.setVisible(true);
	setDim(getSize());
	thread=new Thread(this);
	thread.start();}

 public void paint(Graphics g)
 {Dimension d=getDim();
	 g.clearRect(0,0, d.width ,d.height);
  g.drawString("kills:"+kills,10,10);
  g.drawString("lives:"+lives,100,10);
  g.drawString("level:"+level,200,10);
	invaders.paint(g);
	bullets.paint(g);
	player.paint(g);
 }

 public void run()
 {Thread current=Thread.currentThread();
	while(thread==current)
	try{Dimension d=getDim();
		invaders.incr(d);
		bullets.incr(d);
		frame.repaint();
		thread.sleep(100);
	}catch(Exception ex){ex.printStackTrace();}
 }
////////////////////////////////////////////////////////////////
 /**GO=Graphical Object*/
 static class GO
 {int x,y,dx,dy;boolean on;

	boolean getOn(){return on;}
	void setOn(boolean p)
	{if(p!=on){if(on=p)fireOn();else fireOff();}}

	static Dimension dim=new Dimension(16,16);
	static Dimension dim(){return dim;}

	void paint(Graphics g){g.drawOval(x, y, 16, 16);}

	/**increment*/
	void incr(Dimension d)
	{x+=dx;y+=dy;
	 if(x<0||x+dim.width>=d.width
		||y<0||y+dim.height>=d.height)setOn(false);
	}

	boolean isCollide(GO o)
	{if(on&&o.on
		&&x<o.x+o.dim.width
		&&x+dim.width>o.x
		&&y<o.y+o.dim.height
		&&y+dim.height>o.y)return true;//test intersection
		return false;}

	GO isCollide(GOs a)
	{if(on)for(GO o:a.gos)if(isCollide(o))return o;
		return null;}

	boolean checkCollide(GO o)
	{if(isCollide(o))
	 {fireCollide(o);o.fireCollide(this);
		setOn(false);o.setOn(false);
		return true;}
	 return false;}

	List<GOListener>listeners;

	void addGOListener(GOListener gol)
	{if(listeners==null)listeners=new ArrayList<GOListener>();
	 if(!listeners.contains(gol))listeners.add(gol);}

	void removeGOListener(GOListener gol)
	{if(listeners==null)return;
	 listeners.remove(gol);}

	void fireOn(){if(listeners!=null)for(GOListener l:listeners)l.onOn(this);}
	void fireOff(){if(listeners!=null)for(GOListener l:listeners)l.onOff(this);}
	void fireCollide(GO p2){if(listeners!=null)for(GOListener l:listeners)l.onCollide(this,p2);}

 }//class GO=Graphical Object 
////////////////////////////////////////////////////////////////
 interface GOListener{void onOn(GO go);void onOff(GO go);void onCollide(GO go,GO g2);}
//////////////////////////////////////////////////////////////////
 class Bullet extends GO{Bullet(){x=y=dx=0;dy=-15;}
 void paint(Graphics g){g.drawOval(x, y, 4, 4);}
 }
////////////////////////////////////////////////////////////////
 class Invader extends GO implements GOListener
 {Bullet bullet;long wait;
  Invader(){bullet=new Bullet();bullet.addGOListener(this);dx=3;bullet.dy=5;}

  void paint(Graphics g){if(bullet.on)bullet.paint(g);super.paint(g);}

  void init(int x,int y){this.x=x;this.y=y;wait=(long)Math.random()*30000;setOn(true);}

  void incr(Dimension d)
	{if(!on)return;
	 if((dx>0&&x+dx+dim.width>=d.width)||(dx<0&&x+dx<=0))
	 {y+=dim.height;dx=dx*-1;}
	 if(y>=d.height)y=1;
	 super.incr(d);
	 if(wait>0)wait--;
	 if(wait==0){wait--;shoot();}
	}
  void shoot(){bullet.x=x;bullet.y=y;bullet.setOn(true);}
  public void onOn(GO go){}
  public void onOff(GO go){wait=(long)Math.random()*30000;}
  public void onCollide(GO go,GO g2){wait=(long)Math.random()*10000;}
 }//class Invader
////////////////////////////////////////////////////////////////
 abstract class GOs
 {List<GO>gos;

	void paint(Graphics g)
	{for(GO o:gos)if(o.on)o.paint(g);}

	void incr(Dimension d){for(GO o:gos)if(o.on)o.incr(d);}

	abstract GO instance();

	GOs init(int n){gos=new ArrayList<GO>(n);
		for(int i=0;i<n;i++)gos.add(instance());return this;}

 }//class GOs
////////////////////////////////////////////////////////////////
 class BulletGOs extends GOs
 {GO instance(){return new Bullet();}

  void shootNew(){for(GO b:gos)if(!b.on){b.x=player.x;b.y=player.y;b.setOn(true);return;}}

  void incr(Dimension d)
  {for(GO bullet:gos)
	if(bullet.on)
	{bullet.incr(d);
	  for(GO invader:QD.invaders.gos)
		if(invader.on)
		{	if(bullet.checkCollide(invader))
			{kills++;
				if(--QD.invaders.count<=0)
					QD.invaders.init();
			}
		}
	}
  }
 }//class BulletGOs
////////////////////////////////////////////////////////////////
 class InvaderGOs extends GOs
 {GO instance(){return new Invader();}
	int count,rows,cols;
	Bullet bullet;
  GOs init(int r,int c){init(r*c);rows=r;cols=c;init();return this;}

  void init()
  {Dimension d=Invader.dim(),p=QD.getDim();
   count=rows*cols;level++;
  	if(p==null)QD.setDim(p=getSize());
	int n=gos.size()
	,ox=(p.width-d.width*cols)/cols
	,oy=(p.height-d.height*rows*2)/rows
	,x,y,i=0;
	for(int r=0;r<rows;r++)
	{	y=1+r*oy;
		for(int c=0;c<cols;c++)
		{Invader invader=(Invader)gos.get(i++);
			x=1+c*ox;
			invader.init(x, y);
		}
	}
  }

  void incr(Dimension d)
  {int cnt=0;for(GO invader:gos)if(invader.on)
	{cnt++;	invader.incr(d);
		Bullet iBullet=((Invader)invader).bullet;
		iBullet.incr(d);
		GO b=iBullet.isCollide(QD.bullets);
		if(b==null)b=QD.player;
		if(iBullet.checkCollide(b)&&b==player)
		{lives--;b.setOn(true);}
	}
  if(cnt==0)init();
  }
 }//class InvaderGOs
////////////////////////////////////////////////////////////////

	static Dimension _dim;
	static Dimension getDim(){return _dim;}
	static void setDim(Dimension p)
	{System.out.println("setDim:"+p);
		_dim=p;player.y=p.height-16;}

	@Override	public void componentResized(ComponentEvent e)
	{setDim(this.getSize());}

	@Override	public void mouseMoved(MouseEvent e)
	{player.x=e.getX();}

	@Override	public void mouseReleased(MouseEvent e)
	{((BulletGOs)bullets).shootNew();}

	@Override	public void keyReleased(KeyEvent e)
	{//if fire button pressed
		((BulletGOs)bullets).shootNew();}

	@Override	public void keyTyped(KeyEvent e) {}
	@Override	public void keyPressed(KeyEvent e) {}
	@Override	public void componentMoved(ComponentEvent e) {}
	@Override	public void componentShown(ComponentEvent e) {}
	@Override	public void componentHidden(ComponentEvent e) {}
	@Override	public void mouseDragged(MouseEvent e) {}
	@Override	public void mouseClicked(MouseEvent e) {}
	@Override	public void mousePressed(MouseEvent e) {}
	@Override	public void mouseEntered(MouseEvent e) {}
	@Override	public void mouseExited(MouseEvent e) {}

	/** entry point for main program.	 */
	public static void main(String[] args) {new QD();}

}//QD